// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of dart.core;

/**
 * 数值。表示整型，或浮点数。
 *
 * 当试图继承或实现num类（包括[int]和[double]）的时候，会产生一个编译时错误。
 */
abstract class num implements Comparable<num> {
  /**
   * 测试此数值是否和`other`相等。
   *
   * 当两个操作数都是double时，如果它们表示相同的值，则相等。除了：
   *
   *   * `0.0`和`-0.0`视为相等。它们都表示数值0。
   *   * NaN不等于任何数，包括NaN。如果任何一个操作数是NaN，结果总是false。
   *
   * 当一个操作数是double，另一个是int时，
   * 如果double是一个整数值（有限值，并且没有小数部分），
   * `identical(doubleValue.toInt(), intValue)`返回true，则相等。
   *
   * 当两个操作数都是int时，如果它们的值相同，则相等。
   *
   * 如果`other`并不是一个[num]，返回false。
   *
   * 需要注意，NaN不具有自反性。
   * 这意味着虽然值相等，但并不是严格意义上的相等关系，`operator==`的结果一样。
   * 例如，在[HashSet]中使用NaN时，无法正常工作。
   * 该行为是IEEE-754标准所规定的。
   *
   * 如果能避免使用NaN，那么其它的double则具有真正的相等关系，可以安全使用。
   *
   * 比较函数[compareTo]会区分`0.0`和`-0.0`，并且NaN和NaN视为相等。
   */
  bool operator==(Object other);

  /**
   * 返回数值的哈希码。
   *
   * 哈希码与等式兼容。对于相同数值的[int]和[double]，返回值相同。
   * 因此，0和-0的返回值也是一样的。
   *
   * 无法保证NaN的哈希值。
   */
  int get hashCode;

  /**
   * 与`other`进行比较。
   *
   * 如果`this`小于`other`，返回一个负整数。
   * 如果相等，返回0。如果`this`大于`other`，返回一个正整数。
   *
   * 使用该方法表示的排序是[num]的全排序。
   * 所有不同的double，以及不同的整型是不相等的。
   * 但是，如果它们具有相同的数值，整型也会和double相等。
   *
   * 对于排序，double中的NaN值认为等于它自身，
   * 并且大于任何数值（这与`operator==`操作的行为不同）。
   *
   * 双精度浮点数中，`-0.0`被认为小于`0.0`（以及整型`0`），
   * 但是大于任何非零的负值。
   *
   * 正无穷大于任何有限值(除了它本身和NaN)，而负无穷小于任何其它值。
   *
   * 所有其它值进行比较时，使用它们的数值。
   */
  int compareTo(num other);

  /** 加法运算符。 */
  num operator +(num other);

  /** 减法运算符。 */
  num operator -(num other);

  /** 乘法运算符。 */
  num operator *(num other);

  /**
   * 欧几里得模数运算符，通常称为取余。
   *
   * 返回[欧几里得除法](http://baike.baidu.com/view/1694327.htm)的余数。
   * 欧几里得除法用于计算两个整数`a`和`b`的最大公约数，其中`a`可以表示为
   * `a == b * k + r`（`k`和`r`是两个整数），并且`0 <= r < b.abs()`。
   *
   * 欧几里得除法仅仅是针对整型定义的，但可以很容易扩展为支持double。
   * 在那种情况下，`r`可能是一个非整型的值，但它仍然要验证`0 <= r < |b|`。
   *
   * 返回的`r`始终是正数。
   *
   * 关于截断除法的差数，查看[remainder]。
   */
  num operator %(num other);

  /** 除法运算符。 */
  double operator /(num other);

  /**
   * 截断除法运算符，通常称为取整。
   *
   * 如果其中一个操作数是[double]，
   * 那么`a ~/ b`取整的结果，等同于`(a / b).truncate().toInt()`。
   *
   * 如果两个操作数都是[int]，那么`a ~/ b`执行截断整型除法。
   */
  int operator ~/(num other);

  /** 负值运算符。 */
  num operator -();

 /**
  * 返回对[other]执行截断除法后的差数。
  *
  * 该操作的结果`r`满足：
  * `this == (this ~/ other) * other + r`。
  * 因此，差数`r`的正负与被除数`this`相同。
  */
  num remainder(num other);

  /** 小于关系运算符。 */
  bool operator <(num other);

  /** 小于或等于关系运算符。 */
  bool operator <=(num other);

  /** 大于关系运算符。 */
  bool operator >(num other);

  /** 大于或等于关系运算符。 */
  bool operator >=(num other);

  /** 如果数值是double的NaN值，返回true，否则为false。 */
  bool get isNaN;

  /**
   * 如果数值是负数，返回true，否则为false。
   *
   * 负数小于零和double类型的`-0.0`。
   */
  bool get isNegative;

  /**
   * 如果数值是正无穷或负无穷，返回true，否则为false。
   */
  bool get isInfinite;

  /**
   * 如果数值是有限值，返回true，否则为false。
   *
   * 非有限值仅包括NaN、正无穷和负无穷。
   */
  bool get isFinite;

  /** 返回[num]的绝对值。 */
  num abs();

  /**
   * 根据符号、数值和类型，返回-1，0或1。
   *
   *  * 如果数值小于0，返回-1
   *  * 如果数值大于0，返回1
   *  * 如果数值等于0，返回0
   *
   * 如果数值是double中的NaN值，返回NaN。
   *
   * 返回数值的类型与`this`相同，例如double，`-0.0.sign == -0.0`。

   * 对于所有的数值`n`（除了NaN，因为NaN的`==`操作不等于它自身），
   * 运行结果满足等式：
   *
   *     n == n.sign * n.abs()
   *
   */
  num get sign;

  /**
   * 四舍五入，返回最接近`this`的整数。
   *
   * 如果没有最接近的整数，则返回距离0较远的那个整数：
   *  `(3.5).round() == 4` 和 `(-3.5).round() == -4`。
   *
   * 如果`this`不是有限值，如`NaN`或`INFINITY `，会抛出[UnsupportedError]。
   */
  int round();

  /**
   * 向下取整，或者说向下舍入，返回不大于`this`的最大整数。
   *
   * 如果`this`不是有限值，如`NaN`或`INFINITY `，会抛出[UnsupportedError]。
   */
  int floor();

  /**
   * 向上取整，或者说向上舍入，返回不小于`this`的最小整数。
   *
   * 如果`this`不是有限值，如`NaN`或`INFINITY `，会抛出[UnsupportedError]。
   */
  int ceil();

  /**
   * 截断，通常称为取整。丢弃`this`的任何小数部分，返回整数。
   *
   * 如果`this`不是有限值，如`NaN`或`INFINITY `，会抛出[UnsupportedError]。
   */
  int truncate();

  /**
   * 四舍五入，以double值返回最接近`this`的整数。
   *
   * 如果没有最接近的整数，则返回距离0较远的那个整数：
   *  `(3.5).roundToDouble() == 4` 和 `(-3.5).roundToDouble() == -4`。
   *
   * 如果`this`已经是整数的double形式，包括`-0.0`，或不是一个有限值，
   * 则返回值本身，不做任何修改。
   *
   * 对于舍入，`-0.0`被认为比`0.0`小。因此，`-0.0`被认为比`0.0`更靠近负数。
   * 这就意味着，对于`-0.5 < d < 0.0`范围内的数值`d`，返回结果是`-0.0`。
   *
   * 返回结果总是一个double值。
   * 如果是一个较大的整数，结果可能是一个无限值（double中的`INFINITY`）。
   */
  double roundToDouble();

  /**
   * 向下取整，或者说向下舍入，以double值返回不大于`this`的最大整数。
   *
   * 如果`this`已经是整数的double形式，包括`-0.0`，或不是一个有限值，
   * 则返回值本身，不做任何修改。
   *
   * 对于舍入，`-0.0`被认为比`0.0`小。
   * 对于`0.0 < d < 1.0`范围内的数值`d`，返回结果是`0.0`。
   *
   * 返回结果总是一个double值。
   * 如果是一个较大的整数，结果可能是一个无限值（double中的`INFINITY`）。
   */
  double floorToDouble();

  /**
   * 向上取整，或者说向上舍入，以double值返回不小于`this`的最小整数。
   *
   * 如果`this`已经是整数的double形式，包括`-0.0`，或不是一个有限值，
   * 则返回值本身，不做任何修改。
   *
   * 对于舍入，`-0.0`被认为比`0.0`小。
   * 因此，对于`-1.0 < d < 0.0`范围内的数值`d`，返回结果是`-0.0`。
   *
   * 返回结果总是一个double值。
   * 如果是一个较大的整数，结果可能是一个无限值（double中的`INFINITY`）。
   */
  double ceilToDouble();

  /**
   * 截断，通常称为取整。丢弃`this`的任何小数部分，以double值返回整数。
   *
   * 如果`this`已经是整数的double形式，包括`-0.0`，或不是一个有限值，
   * 则返回值本身，不做任何修改。
   *
   * 对于舍入，`-0.0`被认为比`0.0`小。
   * 因此，对于数值`d`，如果取值范围是`-1.0 < d < 0.0`，返回结果是`-0.0`；
   * 如果取值范围是`0.0 < d < 1.0`，返回结果是`0.0`。
   *
   * 返回结果总是一个double值。
   * 如果是一个较大的整数，结果可能是一个无限值（double中的`INFINITY`）。
   */
  double truncateToDouble();

  /**
   * 限制边界，使[num]在[lowerLimit]-[upperLimit]范围中。
   *
   * * 如果[num]<[lowerLimit]，返回[lowerLimit]
   * * 如果[num]>[upperLimit]，返回[upperLimit]
   * * 否则，返回[num]
   *
   * 比较操作使用的是[compareTo]方法。
   * 因此，需要考虑`-0.0`。这也表明[double.NAN]将被视为最大的double值。
   */
  num clamp(num lowerLimit, num upperLimit);

  /** 将[num]截断成整数，结果作为[int]值返回。 */
  int toInt();

  /**
   * 将[num]作为[double]值返回。
   *
   * 如果数值不能表示为[double]，则返回一个近似值。
   * 对于较大的整数，近似值可能是一个无限值（double中的`INFINITY`）。
   */
  double toDouble();

  /**
   * 返回一个用十进制数形式表示`this`的字符串。
   *
   * 在计算字符串之前，会将`this`转换成[double]。
   *
   * 如果`this`的绝对值大于或等于`10^21`，
   * 那么会通过`this.toStringAsExponential()`方法，用指数来表示。
   * 否则，结果用最接近的字符串表示。
   * 其中[fractionDigits]是小数点后面的位数，如果等于0，则小数位被省略。
   *
   * 参数[fractionDigits]必须是一个整数，并且满足：
   * `0 <= fractionDigits <= 20`.
   *
   * 例如：
   *
   *     1.toStringAsFixed(3);  // 1.000
   *     (4321.12345678).toStringAsFixed(3);  // 4321.123
   *     (4321.12345678).toStringAsFixed(5);  // 4321.12346
   *     123456789012345678901.toStringAsFixed(3);  // 123456789012345683968.000
   *     1000000000000000000000.toStringAsFixed(3); // 1e+21
   *     5.25.toStringAsFixed(0); // 5
   */
  String toStringAsFixed(int fractionDigits);

  /**
   * 返回一个用指数形式表示`this`的字符串。
   *
   * 在计算字符串之前，会将`this`转换成[double]。
   *
   * 如果设置了参数[fractionDigits]，那么它必须是一个整数，并且满足：
   * `0 <= fractionDigits <= 20`。
   * 在这种情况下，字符串包含小数点后[fractionDigits]位。
   * 否则，如果不设置参数，返回的字符串使用最短的、实际位数来表示[this]。
   *
   * 如果[fractionDigits]等于0，则小数点被省略。
   *
   * 例如：
   *
   *     1.toStringAsExponential();       // 1e+0
   *     1.toStringAsExponential(3);      // 1.000e+0
   *     123456.toStringAsExponential();  // 1.23456e+5
   *     123456.toStringAsExponential(3); // 1.235e+5
   *     123.toStringAsExponential(0);    // 1e+2
   */
  String toStringAsExponential([int fractionDigits]);

  /**
   * 返回一个用指定精度[precision]表示`this`的字符串。
   *
   * 在计算字符串之前，会将`this`转换成[double]。
   *
   * 参数[precision]必须是一个整数，并且满足：
   * `1 <= precision <= 21`.
   *
   * 例如：
   *
   *     1.toStringAsPrecision(2);       // 1.0
   *     1e15.toStringAsPrecision(3);    // 1.00+15
   *     1234567.toStringAsPrecision(3); // 1.23e+6
   *     1234567.toStringAsPrecision(9); // 1234567.00
   *     12345678901234567890.toStringAsPrecision(20); // 12345678901234567168
   *     12345678901234567890.toStringAsPrecision(14); // 1.2345678901235e+19
   *     0.00000012345.toStringAsPrecision(15); // 1.23450000000000e-7
   *     0.0000012345.toStringAsPrecision(15);  // 0.00000123450000000000
   */
  String toStringAsPrecision(int precision);

  /**
   * 返回最短的字符串来表示输入数值。
   *
   * 对于`10^-6`（包括）-`10^21`（不包括）范围内的所有[double]，用十进制数形式表示，
   * 并且在小数点后至少有一位。
   * 对于其它所有的[double]，除了特殊值（比如`NaN`，或`Infinity`），
   * 用指数形式表示（查看[toStringAsExponential]）。
   *
   * 对于[double.NAN]，返回`"NaN"`；
   * 对于[double.INFINITY]，返回`"Infinity"`；
   * 对于[double.MINUS_INFINITY]，返回`"-Infinity"`。
   *
   * [int]被转换成用十进制数的形式来表示，不使用小数点。
   *
   * 例如：
   *
   *     (0.000001).toString();  // "0.000001"
   *     (0.0000001).toString(); // "1e-7"
   *     (111111111111111111111.0).toString();  // "111111111111111110000.0"
   *     (100000000000000000000.0).toString();  // "100000000000000000000.0"
   *     (1000000000000000000000.0).toString(); // "1e+21"
   *     (1111111111111111111111.0).toString(); // "1.1111111111111111e+21"
   *     1.toString(); // "1"
   *     111111111111111111111.toString();  // "111111111111111111111"
   *     100000000000000000000.toString();  // "100000000000000000000"
   *     1000000000000000000000.toString(); // "1000000000000000000000"
   *     1111111111111111111111.toString(); // "1111111111111111111111"
   *     1.234e5.toString();   // 123400
   *     1234.5e6.toString();  // 1234500000
   *     12.345e67.toString(); // 1.2345e+68
   *
   * 注意：如果返回的字符串是精确度较高的、唯一标识数值，那么转换可能对输出进行舍入操作。
   * 例如，较高精度的[double]值`9e59`，等于`"899999999999999918767229449717619953810131273674690656206848"`，
   * 但是该方法返回更短的字符串形式（仍然唯一标识）`"9e59"`。
   *
   */
  String toString();

  /**
   * 将[input]作为数值的原义字符串进行解析，返回解析后的值。
   *
   * 该方法首先会尝试将[input]读取成整型（类似于不使用基数的[int.parse]）。
   * 如果失败，它会尝试将[input]解析成double（类似于[double.parse]）。
   * 如果还是失败，则使用[input]为参数调用[onError]，将回调函数的结果作为`parse`的返回值。
   *
   * 如果未设置参数[onError]，它会默认抛出[FormatException]。
   *
   * 对于任何一个数值`n`，该函数满足
   * `identical(n, num.parse(n.toString()))`（除了`n`是`double`中的`NaN`值时）。
   */
  static num parse(String input, [num onError(String input)]) {
    String source = input.trim();
    // TODO(lrn): Optimize to detect format and result type in one check.
    num result = int.parse(source, onError: _returnIntNull);
    if (result != null) return result;
    result = double.parse(source, _returnDoubleNull);
    if (result != null) return result;
    if (onError == null) throw new FormatException(input);
    return onError(input);
  }

  /** Helper functions for [parse]. */
  static int _returnIntNull(String _) => null;
  static double _returnDoubleNull(String _) => null;
}
